#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/devinfo.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/list.h>
#include <linux/printk.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/timer.h>

#define EINT_KEY_IRQ_CNT 4

struct eint_key_irq_info {
    int irq;
    void *dev_id;
    struct timer_list eint_keys_timer;
};

static struct eint_key_irq_info info_irq;
static int first_timer = -1;

static irqreturn_t eint_keys(int irq, void *dev_id)
{
//    printk("%s enter, irq: %d, %s\n", __func__, irq, (char *)dev_id);
    info_irq.irq = irq;
    info_irq.dev_id = dev_id;

    mod_timer(&info_irq.eint_keys_timer, jiffies + HZ/50); // 1 / 50 = 20ms
    
    return IRQ_HANDLED;
}

static void keys_timer_function(struct timer_list *t)
{
    // 通过类似container_of(ptr, type, member)获取传入的数据
    // ptr当前定义的一个结构体中的某个成员变量
    // type成员变量所在结构体的类型
    // member成员变量在结构体中的名字
    
    // from_timer(var, callback_timer, timer_fieldname)
    // var 声明的一个结构体指针
    // callback_timer表示t
    // t在结构体中的名字

    struct eint_key_irq_info *info = NULL;

    if (first_timer < 0) {
        // 第一次触发
        first_timer *= -1;
    } else {
        info = from_timer(info, t, eint_keys_timer);
        printk("%s enter, irq: %d, %s\n", __func__, info->irq, (char *)info->dev_id);
    }
}

static void eint_keys_timer_init(void)
{
#if 0
    //另外种写法
    //NULL不传入数据
    // 低版本kernel < KERNEL_VERSION(4,14,0)

    init_timer(&eint_keys_timer);
    setup_timer(&eint_keys_timer, keys_timer_function, NULL);
    eint_keys_timer.function = keys_timer_function;
    // HZ表示 1s的次数 内核配置CONFIG_HZ 1s中断的次数
	// eint_keys_timer.expires = jiffies + HZ;
	// 不设置周期，不会激活定时器
	// eint_keys_timer.expires = jiffies;
	// 设置周期, 为最大
	// eint_keys_timer.expires = ~0;
	// 设置数据
	// eint_keys_timer.data = (long)&val;
	add_timer(&eint_keys_timer);
#endif

    timer_setup(&info_irq.eint_keys_timer, keys_timer_function, 0);
    // 马上就会触发
    //eint_keys_timer.expires = jiffies + HZ * 1000;
    //eint_keys_timer.expires = jiffies + ~0;
    //eint_keys_timer.expires = ~0;
    add_timer(&info_irq.eint_keys_timer);
}

/**
 * 当驱动和硬件信息匹配成功之后，就会调用probe函数，驱动所有的资源的注册和初始化全部放在probe函数中
 */
static int eint_key_probe(struct platform_device *pdev)
{
    struct device_node *np = NULL;
    int ret = 0;
    int i = 0;
    char *key_name = NULL;
    
    np = pdev->dev.of_node;
    // 通过platform_device获取设备节点
    if (np == NULL) {
        dev_err(&pdev->dev, "eint_key_probe failed");
        return -EINVAL;
    }

    // 通过irq_of_parse_and_map节点获取中断号
    for (i = 0; i < EINT_KEY_IRQ_CNT; i++) {
        // 获取中断资源
        ret = irq_of_parse_and_map(np, i);
        if (ret <= 0) {
            dev_err(&pdev->dev, "cannot find irq index %d\n", i);
            return ret;
        }

        key_name = kasprintf(GFP_KERNEL, "key-%d", i+1);
        // 注册中断号
        ret = devm_request_irq(&pdev->dev, ret, eint_keys, 0, key_name, key_name);
        if (ret != 0) {
            dev_err(&pdev->dev, "cannot claim irq %d\n", ret);
            return ret;
        }
    }

    eint_keys_timer_init();

    return 0;
}

/**
 * 硬件信息被移除了，或者驱动被卸载了，全部要释放，释放资源的操作就放在该函数中
 */
static int eint_key_remove(struct platform_device *pdev)
{
    del_timer(&info_irq.eint_keys_timer);

    return 0;
}

/**
 * 用于设备树匹配
 */
static const struct of_device_id eint_key_dt_match[] = {
    { .compatible = "eint-keys", },
    {},
};

/**
 * platform驱动
 */
static struct platform_driver eint_key_driver = {
    .probe = eint_key_probe,
    .remove = eint_key_remove,
    .driver = {
        .name = "eint_key",
        .of_match_table = eint_key_dt_match,  // 匹配列表
    }
};


static int eint_key_init(void)
{
    int ret = platform_driver_register(&eint_key_driver);
    if (ret < 0) {
        pr_err("eint_key_init platform_driver_register failed");
        return -EINVAL;
    }

    return 0;
}

/**
 * platform驱动模块出口
 */
static void eint_key_exit(void)
{
    platform_driver_unregister(&eint_key_driver);
}

module_init(eint_key_init);
module_exit(eint_key_exit);

MODULE_AUTHOR("huang");
MODULE_DESCRIPTION("jz2440 eint key driver");
MODULE_LICENSE("GPL");



